Purpose:

Runs survival analysis models using splicing cluster assignment and 1) single exon splicing burden index (SBI) or 2) KEGG Spliceosome GSVA scores as a predictor

Usage

Uses a wrapper function (survival_analysis) from utils folder.

Setup

Packages and functions

Load packages, set directory paths and call setup script

library(tidyverse)
library(survival)
library(ggpubr)
library(ggplot2)
library(patchwork)

root_dir <- rprojroot::find_root(rprojroot::has_dir(".git"))

data_dir <- file.path(root_dir, "data")
analysis_dir <- file.path(root_dir, "analyses", "survival")
input_dir <- file.path(analysis_dir, "results")
results_dir <- file.path(analysis_dir, "results")
plot_dir <- file.path(analysis_dir, "plots")

# If the input and results directories do not exist, create it
if (!dir.exists(results_dir)) {
  dir.create(results_dir, recursive = TRUE)
}

source(file.path(analysis_dir, "util", "survival_models.R"))

Set metadata and cluster assignment file paths

metadata_file <- file.path(input_dir, "splicing_indices_with_survival.tsv")

cluster_file <- file.path(root_dir, "analyses",
                          "sample-psi-clustering", "results",
                          "sample-cluster-metadata-top-5000-events-stranded.tsv")

kegg_scores_stranded_file <- file.path(root_dir, "analyses",
                          "sample-psi-clustering", "results",
                          "gsva_output_stranded.tsv")

Wrangle data Add cluster assignment and spliceosome gsva scores to metadata and define column lgg_group (LGG or non_LGG)

metadata <- read_tsv(metadata_file)
Rows: 707 Columns: 24
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr  (9): Kids_First_Biospecimen_ID, Histology, Kids_First_Participant_ID, molecular_subtype, extent_of_tumor_...
dbl (15): Total, AS_neg, AS_pos, AS_total, SI_A3SS, SI_A5SS, SI_RI, SI_SE, SI_Total, EFS_days, OS_days, age_at...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
clusters <- read_tsv(cluster_file) %>%
  dplyr::rename(Kids_First_Biospecimen_ID = sample_id)
Rows: 752 Columns: 8
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (6): sample_id, plot_group, plot_group_hex, RNA_library, molecular_subtype, plot_group_n
dbl (2): cluster, group_n

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
gsva_scores <- read_tsv(kegg_scores_stranded_file) %>%
  dplyr::filter(geneset == "KEGG_SPLICEOSOME") %>%
  dplyr::rename(spliceosome_gsva_score = score)
Rows: 23312 Columns: 3
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): sample_id, geneset
dbl (1): score

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# how many clusters?
n_clust <- length(unique(clusters$cluster))

metadata <- metadata %>%
  right_join(clusters %>% dplyr::select(Kids_First_Biospecimen_ID,
                                       cluster)) %>%
  left_join(gsva_scores %>% dplyr::select(sample_id,
                                          spliceosome_gsva_score),
            by = c("Kids_First_Biospecimen_ID" = "sample_id")) %>% 
  dplyr::mutate(cluster = glue::glue("Cluster {cluster}")) %>%
  dplyr::mutate(cluster = fct_relevel(cluster,
                                               paste0("Cluster ", 1:n_clust))) %>%
  dplyr::mutate(lgg_group = case_when(
    Histology == "Low-grade glioma" ~ "LGG",
    TRUE ~ "non-LGG"
  )) %>%
  dplyr::mutate(SBI = SI_Total * 10) %>%
  dplyr::mutate(age_at_diagnosis_years = age_at_diagnosis_days/365.25)
Joining with `by = join_by(Kids_First_Biospecimen_ID)`

Generate coxph models including extent of tumor resection, lgg group, and cluster assignment and SBI as covariates

add_model_os <- fit_save_model(metadata[!metadata$extent_of_tumor_resection %in% c("Not Reported", "Unavailable"),],
                              terms = "extent_of_tumor_resection+lgg_group+cluster+age_at_diagnosis_years+SBI",
                               file.path(results_dir, "cox_OS_additive_terms_resection_lgg_group_cluster_SBI.RDS"),
                               "multivariate",
                               years_col = "OS_years",
                               status_col = "OS_status")

forest_os <- plotForest(readRDS(file.path(results_dir, "cox_OS_additive_terms_resection_lgg_group_cluster_SBI.RDS")))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).

forest_os

ggsave(file.path(plot_dir, "forest_add_OS_resection_lgg_group_cluster_assignment_SBI.pdf"),
       forest_os,
       width = 10, height = 6, units = "in",
       device = "pdf")


add_model_efs <- fit_save_model(metadata[!metadata$extent_of_tumor_resection %in% c("Not Reported", "Unavailable"),],
                              terms = "extent_of_tumor_resection+lgg_group+cluster+age_at_diagnosis_years+SBI",
                               file.path(results_dir, "cox_EFS_additive_terms_resection_lgg_group_cluster_SBI.RDS"),
                               "multivariate",
                               years_col = "EFS_years",
                               status_col = "EFS_status")

forest_efs <- plotForest(readRDS(file.path(results_dir, "cox_EFS_additive_terms_resection_lgg_group_cluster_SBI.RDS")))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).

forest_efs

ggsave(file.path(plot_dir, "forest_add_EFS_resection_lgg_group_cluster_assignment_SBI.pdf"),
       forest_efs,
       width = 10, height = 6, units = "in",
       device = "pdf")

repeat analysis, replacing SBI with KEGG spliceosome gsva score

add_model_os <- fit_save_model(metadata[!metadata$extent_of_tumor_resection %in% c("Not Reported", "Unavailable"),],
                              terms = "extent_of_tumor_resection+lgg_group+cluster+age_at_diagnosis_years+spliceosome_gsva_score",
                               file.path(results_dir, "cox_OS_additive_terms_resection_lgg_group_cluster_spliceosome_score.RDS"),
                               "multivariate",
                               years_col = "OS_years",
                               status_col = "OS_status")

forest_os <- plotForest(readRDS(file.path(results_dir, "cox_OS_additive_terms_resection_lgg_group_cluster_spliceosome_score.RDS")))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).

forest_os

ggsave(file.path(plot_dir, "forest_add_OS_resection_lgg_group_cluster_assignment_spliceosome_score.pdf"),
       forest_os,
       width = 10, height = 6, units = "in",
       device = "pdf")

models <- c("spliceosome_gsva_score", "SBI")

for (each in models) {
  int_model_efs <- fit_save_model(metadata[!metadata$extent_of_tumor_resection %in% c("Not Reported", "Unavailable"),],
                                terms = paste0("extent_of_tumor_resection+lgg_group+cluster*", each, "+age_at_diagnosis_years"),
                                 file.path(results_dir, paste0("cox_EFS_interaction_terms_resection_lgg_group_cluster_", each, ".RDS")),
                                 "multivariate",
                                 years_col = "EFS_years",
                                 status_col = "EFS_status")
  
  int_forest_efs <- plotForest(readRDS(file.path(results_dir, paste0("cox_EFS_interaction_terms_resection_lgg_group_cluster_", each, ".RDS"))))
  
  int_forest_efs
  
  ggsave(file.path(plot_dir, paste0("forest_int_EFS_resection_lgg_group_cluster_assignment_", each, ".pdf")),
         int_forest_efs,
         width = 10, height = 6, units = "in",
         device = "pdf")

  int_model_os <- fit_save_model(metadata[!metadata$extent_of_tumor_resection %in% c("Not Reported", "Unavailable"),],
                                terms = paste0("extent_of_tumor_resection+lgg_group+cluster*", each, "+age_at_diagnosis_years"),
                                 file.path(results_dir, paste0("cox_OS_interaction_terms_resection_lgg_group_cluster_", each, ".RDS")),
                                 "multivariate",
                                 years_col = "OS_years",
                                 status_col = "OS_status")
  
  int_forest_os <- plotForest(readRDS(file.path(results_dir, paste0("cox_OS_interaction_terms_resection_lgg_group_cluster_", each, ".RDS"))))
  
  int_forest_os
  
  ggsave(file.path(plot_dir, paste0("forest_int_OS_resection_lgg_group_cluster_assignment_", each, ".pdf")),
         int_forest_os,
         width = 10, height = 6, units = "in",
         device = "pdf")
  
}
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).

add_model_efs <- fit_save_model(metadata[!metadata$extent_of_tumor_resection %in% c("Not Reported", "Unavailable"),],
                              terms = "extent_of_tumor_resection+lgg_group+cluster+age_at_diagnosis_years+spliceosome_gsva_score",
                               file.path(results_dir, "cox_EFS_additive_terms_resection_lgg_group_cluster_spliceosome_score.RDS"),
                               "multivariate",
                               years_col = "EFS_years",
                               status_col = "EFS_status")

forest_efs <- plotForest(readRDS(file.path(results_dir, "cox_EFS_additive_terms_resection_lgg_group_cluster_spliceosome_score.RDS")))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).

forest_efs

ggsave(file.path(plot_dir, "forest_add_EFS_resection_lgg_group_cluster_assignment_spliceosome_score.pdf"),
       forest_efs,
       width = 10, height = 6, units = "in",
       device = "pdf")

Subset metadata for LGG, and only include clusters with >= 10 samples

lgg <- metadata %>%
  dplyr::filter(Histology == "Low-grade glioma") %>%
  dplyr::mutate(cluster = factor(cluster)) %>%
  dplyr::mutate(mol_sub_group = fct_relevel(mol_sub_group, c("Wildtype", "BRAF V600E", "BRAF fusion",
                                                                "Other alteration", "SEGA"
                                                                )))

retain_clusters_lgg <- lgg %>%
  dplyr::count(cluster) %>%
  filter(n >= 10) %>%
  pull(cluster)

lgg <- lgg %>%
  filter(cluster %in% retain_clusters_lgg) %>%
    dplyr::mutate(cluster = factor(cluster))

Generate coxph models including covariates extent_of_tumor_resection, mol_sub_group, cluster, and SBI and plot

# identify LGG clusters
lgg_clusters <- metadata %>%
  filter(lgg_group == "LGG") %>%
  mutate(cluster = as.integer(gsub("cluster", "", cluster))) %>%
  pull(cluster) %>%
  sort() %>%
  unique()
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `cluster = as.integer(gsub("cluster", "", cluster))`.
Caused by warning:
! NAs introduced by coercion
add_model_lgg_efs <- fit_save_model(lgg[!lgg$cluster %in% lgg_clusters & !lgg$extent_of_tumor_resection %in% c("Not Reported", "Unavailable"),],
                              terms = "extent_of_tumor_resection+mol_sub_group+cluster+age_at_diagnosis_years+SBI",
                               file.path(results_dir, "cox_lgg_EFS_additive_terms_resection_subtype_cluster_SBI.RDS"),
                               "multivariate",
                               years_col = "EFS_years",
                               status_col = "EFS_status")
Warning in coxph.fit(X, Y, istrat, offset, init, control, weights = weights,  :
  Loglik converged before variable  6,8 ; coefficient may be infinite. 
forest_lgg_efs <- plotForest(readRDS(file.path(results_dir, "cox_lgg_EFS_additive_terms_resection_subtype_cluster_SBI.RDS")))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).

forest_lgg_efs

ggsave(file.path(plot_dir, "forest_add_EFS_LGG_resection_subtype_cluster_assignment_SBI.pdf"),
       forest_lgg_efs,
       width = 10, height = 6, units = "in",
       device = "pdf")

repeat analysis replacing SBI with spliceosome_gsva_score

add_model_lgg_efs <- fit_save_model(lgg[!lgg$cluster %in% lgg_clusters & !lgg$extent_of_tumor_resection %in% c("Not Reported", "Unavailable"),],
                              terms = "extent_of_tumor_resection+mol_sub_group+cluster+age_at_diagnosis_years+spliceosome_gsva_score",
                               file.path(results_dir, "cox_lgg_EFS_additive_terms_resection_subtype_cluster_spliceosome_score.RDS"),
                               "multivariate",
                               years_col = "EFS_years",
                               status_col = "EFS_status")
Warning in coxph.fit(X, Y, istrat, offset, init, control, weights = weights,  :
  Loglik converged before variable  6,8 ; coefficient may be infinite. 
forest_lgg_efs <- plotForest(readRDS(file.path(results_dir, "cox_lgg_EFS_additive_terms_resection_subtype_cluster_spliceosome_score.RDS")))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).

forest_lgg_efs

ggsave(file.path(plot_dir, "forest_add_EFS_LGG_resection_subtype_cluster_assignment_spliceosome_score.pdf"),
       forest_lgg_efs,
       width = 10, height = 6, units = "in",
       device = "pdf")

Subset metadata for HGG and retain cluster with n >= 10

hgg <- metadata %>%
  dplyr::filter(Histology %in% c("Other high-grade glioma", "DIPG or DMG")) %>%
  dplyr::mutate(cluster = factor(cluster)) %>%
  dplyr::mutate(mol_sub_group = fct_relevel(mol_sub_group, c("HGG, H3 wildtype", "HGG, H3 wildtype, TP53",
                                                             "DMG, H3 K28", "DMG, H3 K28, TP53",
                                                                "DHG, H3 G35", "DHG, H3 G35, TP53",
                                                                "HGG, IDH, TP53", "HGG, PXA", "HGG, PXA, TP53", 
                                                                "IHG, ALK-altered", "IHG, NTRK-altered",
                                                                "IHG, ROS1-altered"
                                                                ))) %>%
  dplyr::filter(!is.na(OS_days) | !is.na(EFS_days))
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `mol_sub_group = fct_relevel(...)`.
Caused by warning:
! 1 unknown level in `f`: HGG, PXA, TP53
retain_clusters_hgg <- hgg %>%
  dplyr::count(cluster) %>%
  filter(n >= 10) %>%
  pull(cluster)

hgg <- hgg %>%
  filter(cluster %in% retain_clusters_hgg) %>%
  dplyr::mutate(cluster = factor(cluster)) %>%
    dplyr::mutate(SI_group = case_when(
      SBI > summary(SBI)["3rd Qu."] ~ "High SBI",
      SBI < summary(SBI)["1st Qu."] ~ "Low SBI",
      TRUE ~ NA_character_
    )) %>%
  dplyr::mutate(spliceosome_group = case_when(
      spliceosome_gsva_score > summary(spliceosome_gsva_score)["3rd Qu."] ~ "Splice GSVA 4th Q",
      spliceosome_gsva_score > summary(spliceosome_gsva_score)["Median"] ~ "Splice GSVA 3rd Q",
      spliceosome_gsva_score > summary(spliceosome_gsva_score)["1st Qu."] ~ "Splice GSVA 2nd Q",
      TRUE ~ "Splice GSVA 1st Q"
    )) %>%
  dplyr::mutate(SI_group = fct_relevel(SI_group,
                                                 c("High SBI", "Low SBI"))) %>%
  dplyr::mutate(spliceosome_group = fct_relevel(spliceosome_group,
                                                 c("Splice GSVA 1st Q", 
                                                   "Splice GSVA 2nd Q", 
                                                   "Splice GSVA 3rd Q",
                                                   "Splice GSVA 4th Q")))

Generate HGG KM models with spliceosome_group as covariate

# Generate kaplan meier survival models for OS and EFS, and save outputs
hgg_kap_os <- survival_analysis(
  metadata  = hgg %>% dplyr::filter(!is.na(spliceosome_group)),
  ind_var = "spliceosome_group",
  test = "kap.meier",
  metadata_sample_col = "Kids_First_Biospecimen_ID",
  days_col = "OS_days",
  status_col = "OS_status"
)
Testing model: survival::Surv(OS_days, OS_status) ~ spliceosome_group with kap.meier
readr::write_rds(hgg_kap_os,
                 file.path(results_dir, "logrank_hgg_OS_splice_group.RDS"))

hgg_kap_efs <- survival_analysis(
  metadata  = hgg %>% dplyr::filter(!is.na(spliceosome_group)),
  ind_var = "spliceosome_group",
  test = "kap.meier",
  metadata_sample_col = "Kids_First_Biospecimen_ID",
  days_col = "EFS_days",
  status_col = "EFS_status"
)
Testing model: survival::Surv(EFS_days, EFS_status) ~ spliceosome_group with kap.meier
readr::write_rds(hgg_kap_efs,
                 file.path(results_dir, "logrank_hgg_EFS_splice_group.RDS"))

Generate KM plots

km_hgg_os_plot <- plotKM(model = hgg_kap_os,
                    variable = "spliceosome_group",
                    combined = F, 
                    title = "HGG, overall survival",
                    p_pos = "topright")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
ggsave(file.path(plot_dir, "km_hgg_OS_spliceosome_score.pdf"),
       km_hgg_os_plot,
       width = 9, height = 5, units = "in",
       device = "pdf")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's colour values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
km_hgg_efs_plot <- plotKM(model = hgg_kap_efs,
                    variable = "spliceosome_group",
                    combined = F, 
                    title = "HGG, event-free survival",
                    p_pos = "topright")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
ggsave(file.path(plot_dir, "km_hgg_EFS_spliceosome_score.pdf"), 
       km_hgg_efs_plot,
       width = 9, height = 5, units = "in",
       device = "pdf")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's colour values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.

Generate coxph models for HGG including covariates mol_sub_group cluster, and SBI, and plot

add_model_hgg_os <- fit_save_model(hgg,
                              terms = "mol_sub_group+cluster+age_at_diagnosis_years+SBI",
                               file.path(results_dir, "cox_hgg_OS_additive_terms_subtype_cluster_SBI.RDS"),
                               "multivariate",
                               years_col = "OS_years",
                               status_col = "OS_status")
Warning in coxph.fit(X, Y, istrat, offset, init, control, weights = weights,  :
  Loglik converged before variable  9 ; coefficient may be infinite. 
forest_hgg_os <- plotForest(readRDS(file.path(results_dir, "cox_hgg_OS_additive_terms_subtype_cluster_SBI.RDS")))
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_text()`).

forest_hgg_os

ggsave(file.path(plot_dir, "forest_add_OS_HGG_subtype_cluster_assignment_SBI.pdf"),
       forest_hgg_os,
       width = 9, height = 5, units = "in",
       device = "pdf")


add_model_hgg_efs <- fit_save_model(hgg,
                              terms = "mol_sub_group+cluster+age_at_diagnosis_years+SBI",
                               file.path(results_dir, "cox_hgg_EFS_additive_terms_subtype_cluster_SBI.RDS"),
                               "multivariate",
                               years_col = "EFS_years",
                               status_col = "EFS_status")

forest_hgg_efs <- plotForest(readRDS(file.path(results_dir, "cox_hgg_EFS_additive_terms_subtype_cluster_SBI.RDS")))
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_text()`).
ggsave(file.path(plot_dir, "forest_add_EFS_HGG_subtype_cluster_assignment_SBI.pdf"),
       forest_hgg_efs,
       width = 9, height = 5, units = "in",
       device = "pdf")

Repeat analysis replacing SBI with spliceosome_gsva_score

add_model_hgg_os <- fit_save_model(hgg,
                              terms = "mol_sub_group+cluster+age_at_diagnosis_years+spliceosome_gsva_score",
                               file.path(results_dir, "cox_hgg_OS_additive_terms_subtype_cluster_spliceosome_score.RDS"),
                               "multivariate",
                               years_col = "OS_years",
                               status_col = "OS_status")
Warning in coxph.fit(X, Y, istrat, offset, init, control, weights = weights,  :
  Loglik converged before variable  9 ; coefficient may be infinite. 
forest_hgg_os <- plotForest(readRDS(file.path(results_dir, "cox_hgg_OS_additive_terms_subtype_cluster_spliceosome_score.RDS")))
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_text()`).

forest_hgg_os

ggsave(file.path(plot_dir, "forest_add_OS_HGG_subtype_cluster_assignment_spliceosome_score.pdf"),
       forest_hgg_os,
       width = 9, height = 5, units = "in",
       device = "pdf")


add_model_hgg_efs <- fit_save_model(hgg,
                              terms = "mol_sub_group+cluster+age_at_diagnosis_years+spliceosome_gsva_score",
                               file.path(results_dir, "cox_hgg_EFS_additive_terms_subtype_cluster_spliceosome_score.RDS"),
                               "multivariate",
                               years_col = "EFS_years",
                               status_col = "EFS_status")

forest_hgg_efs <- plotForest(readRDS(file.path(results_dir, "cox_hgg_EFS_additive_terms_subtype_cluster_spliceosome_score.RDS")))
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_text()`).
ggsave(file.path(plot_dir, "forest_add_EFS_HGG_subtype_cluster_assignment_spliceosome_score.pdf"),
       forest_hgg_efs,
       width = 9, height = 5, units = "in",
       device = "pdf")

Filter for cluster 6

cluster6_df <- metadata %>%
  dplyr::filter(cluster == "Cluster 6",
                !is.na(EFS_days)) %>%
  dplyr::mutate(SI_group = case_when(
      SBI > summary(SBI)["3rd Qu."] ~ "High SBI",
      SBI < summary(SBI)["1st Qu."] ~ "Low SBI",
      TRUE ~ NA_character_
    )) %>%
  dplyr::mutate(spliceosome_group = case_when(
      spliceosome_gsva_score > summary(spliceosome_gsva_score)["3rd Qu."] ~ "Splice GSVA 4th Q",
      spliceosome_gsva_score > summary(spliceosome_gsva_score)["Median"] ~ "Splice GSVA 3rd Q",
      spliceosome_gsva_score > summary(spliceosome_gsva_score)["1st Qu."] ~ "Splice GSVA 2nd Q",
      TRUE ~ "Splice GSVA 1st Q"
    )) %>%
  dplyr::mutate(SI_group = fct_relevel(SI_group,
                                                 c("High SBI", "Low SBI"))) %>%
  dplyr::mutate(spliceosome_group = fct_relevel(spliceosome_group,
                                                 c("Splice GSVA 1st Q", 
                                                   "Splice GSVA 2nd Q", 
                                                   "Splice GSVA 3rd Q",
                                                   "Splice GSVA 4th Q")))

Generate KM models with SI_group as covariate

# Generate kaplan meier survival models for OS and EFS, and save outputs
c6_si_kap_os <- survival_analysis(
  metadata  = cluster6_df %>% dplyr::filter(!is.na(SI_group)),
  ind_var = "SI_group",
  test = "kap.meier",
  metadata_sample_col = "Kids_First_Biospecimen_ID",
  days_col = "OS_days",
  status_col = "OS_status"
)
Testing model: survival::Surv(OS_days, OS_status) ~ SI_group with kap.meier
readr::write_rds(c6_si_kap_os,
                 file.path(results_dir, "logrank_cluster6_OS_SBI.RDS"))

c6_si_kap_efs <- survival_analysis(
  metadata  = cluster6_df %>% dplyr::filter(!is.na(SI_group)),
  ind_var = "SI_group",
  test = "kap.meier",
  metadata_sample_col = "Kids_First_Biospecimen_ID",
  days_col = "EFS_days",
  status_col = "EFS_status"
)
Testing model: survival::Surv(EFS_days, EFS_status) ~ SI_group with kap.meier
readr::write_rds(c6_si_kap_efs,
                 file.path(results_dir, "logrank_cluster6_EFS_SBI.RDS"))

Generate Cluster 6 KM SI_group plots

km_c6_si_os_plot <- plotKM(model = c6_si_kap_os,
                    variable = "SI_group",
                    combined = F, 
                    title = "Cluster 6, overall survival",
                    p_pos = "topright")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
ggsave(file.path(plot_dir, "km_cluster6_OS_sbi_group.pdf"),
       km_c6_si_os_plot,
       width = 8, height = 5, units = "in",
       device = "pdf")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's colour values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
km_c6_si_efs_plot <- plotKM(model = c6_si_kap_efs,
                    variable = "SI_group",
                    combined = F, 
                    title = "Cluster 6, event-free survival",
                    p_pos = "topright")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
ggsave(file.path(plot_dir, "km_cluster6_EFS_sbi_group.pdf"), 
       km_c6_si_efs_plot,
       width = 8, height = 5, units = "in",
       device = "pdf")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's colour values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.

Generate KM models with spliceosome_group as covariate

# Generate kaplan meier survival models for OS and EFS, and save outputs
c6_splice_kap_os <- survival_analysis(
  metadata  = cluster6_df %>% 
    dplyr::filter(spliceosome_group %in% c("Splice GSVA 4th Q", "Splice GSVA 1st Q")) %>%
    dplyr::mutate(spliceosome_group = factor(spliceosome_group,
                                                  levels = c("Splice GSVA 1st Q", "Splice GSVA 4th Q"))),
  ind_var = "spliceosome_group",
  test = "kap.meier",
  metadata_sample_col = "Kids_First_Biospecimen_ID",
  days_col = "OS_days",
  status_col = "OS_status"
)
Testing model: survival::Surv(OS_days, OS_status) ~ spliceosome_group with kap.meier
readr::write_rds(c6_splice_kap_os,
                 file.path(results_dir, "logrank_cluster6_OS_splice_group.RDS"))

c6_splice_kap_efs <- survival_analysis(
  metadata  = cluster6_df %>% 
    dplyr::filter(spliceosome_group %in% c("Splice GSVA 4th Q", "Splice GSVA 1st Q")) %>%
    dplyr::mutate(spliceosome_group = factor(spliceosome_group,
                                                  levels = c("Splice GSVA 1st Q", "Splice GSVA 4th Q"))),
  ind_var = "spliceosome_group",
  test = "kap.meier",
  metadata_sample_col = "Kids_First_Biospecimen_ID",
  days_col = "EFS_days",
  status_col = "EFS_status"
)
Testing model: survival::Surv(EFS_days, EFS_status) ~ spliceosome_group with kap.meier
readr::write_rds(c6_splice_kap_efs,
                 file.path(results_dir, "logrank_cluster6_EFS_splice_group.RDS"))

Generate Cluster 6 KM spliceosome_group plots

km_c6_splice_os_plot <- plotKM(model = c6_splice_kap_os,
                    variable = "spliceosome_group",
                    combined = F, 
                    title = "Cluster 6, overall survival",
                    p_pos = "topright")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
ggsave(file.path(plot_dir, "km_cluster6_OS_splice_group.pdf"),
       km_c6_splice_os_plot,
       width = 9, height = 5, units = "in",
       device = "pdf")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's colour values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
km_c6_splice_efs_plot <- plotKM(model = c6_splice_kap_efs,
                    variable = "spliceosome_group",
                    combined = F, 
                    title = "Cluster 6, event-free survival",
                    p_pos = "topright")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
ggsave(file.path(plot_dir, "km_cluster6_EFS_splice_group.pdf"), 
       km_c6_splice_efs_plot,
       width = 9, height = 5, units = "in",
       device = "pdf")
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's colour values.
Warning: No shared levels found between `names(values)` of the manual scale and the data's fill values.

Assess EFS and OS by SBI or spliceosome GSVA score in multivariate models and generate forest plots

add_model_c6_efs <- fit_save_model(cluster6_df %>% 
                                      dplyr::filter(extent_of_tumor_resection != "Unavailable",
                                                    spliceosome_group %in% c("Splice GSVA 4th Q", "Splice GSVA 1st Q")) %>%
                                      dplyr::mutate(Histology = fct_relevel(Histology,
                                                                            c("Other high-grade glioma", "Atypical Teratoid Rhabdoid Tumor",
                                                                              "DIPG or DMG", "Ependymoma", "Mesenchymal tumor",
                                                                              "Other CNS embryonal tumor", "Low-grade glioma"))),
                              terms = "extent_of_tumor_resection+age_at_diagnosis_years+Histology+spliceosome_group",
                               file.path(results_dir, "cox_hgg_EFS_additive_terms_subtype_cluster_spliceosome_score.RDS"),
                               "multivariate",
                               years_col = "EFS_years",
                               status_col = "EFS_status")
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `Histology = fct_relevel(...)`.
Caused by warning:
! 2 unknown levels in `f`: Ependymoma and Low-grade glioma
forest_c6_spliceosome_efs <- plotForest(readRDS(file.path(results_dir, "cox_hgg_EFS_additive_terms_subtype_cluster_spliceosome_score.RDS")))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_text()`).
ggsave(file.path(plot_dir, "forest_add_EFS_cluster6_histology_resection_spliceosome_group.pdf"),
       forest_c6_spliceosome_efs,
       width = 9, height = 4, units = "in",
       device = "pdf")


add_model_c6_os <- fit_save_model(cluster6_df %>% 
                                    dplyr::filter(!extent_of_tumor_resection %in% c("Not Reported", "Unavailable")) %>%
                                    dplyr::mutate(Histology = fct_relevel(Histology,
                                                                            c("Other high-grade glioma", "Atypical Teratoid Rhabdoid Tumor",
                                                                              "DIPG or DMG", "Ependymoma", "Mesenchymal tumor",
                                                                              "Other CNS embryonal tumor", "Low-grade glioma"))),
                              terms = "extent_of_tumor_resection+age_at_diagnosis_years+Histology+SBI",
                               file.path(results_dir, "cox_hgg_OS_additive_terms_subtype_cluster_si_group.RDS"),
                               "multivariate",
                               years_col = "OS_years",
                               status_col = "OS_status")
Warning in coxph.fit(X, Y, istrat, offset, init, control, weights = weights,  :
  Loglik converged before variable  9 ; coefficient may be infinite. 
forest_c6_si_os <- plotForest(readRDS(file.path(results_dir, "cox_hgg_OS_additive_terms_subtype_cluster_si_group.RDS")))
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_errorbarh()`).
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_text()`).
ggsave(file.path(plot_dir, "forest_add_OS_cluster6_histology_resection_si.pdf"),
       forest_c6_si_os,
       width = 9, height = 4, units = "in",
       device = "pdf")

Print session info

sessionInfo()
R version 4.4.0 (2024-04-24)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so;  LAPACK version 3.10.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Etc/UTC
tzcode source: system (glibc)

attached base packages:
[1] grid      stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggforce_0.4.2         RColorBrewer_1.1-3    viridis_0.6.5         viridisLite_0.4.2     ggtext_0.1.2         
 [6] data.table_1.16.4     vroom_1.6.5           reshape2_1.4.4        ggthemes_5.1.0        circlize_0.4.16      
[11] ComplexHeatmap_2.20.0 gtools_3.9.5          survminer_0.4.9       patchwork_1.2.0       ggpubr_0.6.0         
[16] survival_3.7-0        lubridate_1.9.4       forcats_1.0.0         stringr_1.5.1         dplyr_1.1.4          
[21] purrr_1.0.4           readr_2.1.5           tidyr_1.3.1           tibble_3.2.1          ggplot2_3.5.1        
[26] tidyverse_2.0.0      

loaded via a namespace (and not attached):
 [1] gridExtra_2.3       rlang_1.1.5         magrittr_2.0.3      clue_0.3-65         GetoptLong_1.0.5   
 [6] matrixStats_1.3.0   compiler_4.4.0      png_0.1-8           systemfonts_1.2.1   vctrs_0.6.5        
[11] pkgconfig_2.0.3     shape_1.4.6.1       crayon_1.5.3        fastmap_1.2.0       backports_1.5.0    
[16] labeling_0.4.3      KMsurv_0.1-5        utf8_1.2.4          rmarkdown_2.29      markdown_1.13      
[21] tzdb_0.4.0          ragg_1.3.3          bit_4.5.0.1         xfun_0.50           cachem_1.1.0       
[26] jsonlite_1.8.9      tweenr_2.0.3        broom_1.0.7         parallel_4.4.0      cluster_2.1.8      
[31] R6_2.6.1            bslib_0.9.0         stringi_1.8.4       car_3.1-2           jquerylib_0.1.4    
[36] Rcpp_1.0.14         iterators_1.0.14    knitr_1.49          zoo_1.8-12          IRanges_2.38.1     
[41] Matrix_1.7-2        splines_4.4.0       timechange_0.3.0    tidyselect_1.2.1    yaml_2.3.10        
[46] rstudioapi_0.17.1   abind_1.4-5         doParallel_1.0.17   codetools_0.2-20    lattice_0.22-6     
[51] plyr_1.8.9          withr_3.0.2         evaluate_1.0.3      polyclip_1.10-6     xml2_1.3.6         
[56] survMisc_0.5.6      pillar_1.10.1       colorblindr_0.1.0   carData_3.0-5       foreach_1.5.2      
[61] stats4_4.4.0        generics_0.1.3      rprojroot_2.0.4     S4Vectors_0.42.1    hms_1.1.3          
[66] commonmark_1.9.2    munsell_0.5.1       scales_1.3.0        xtable_1.8-4        glue_1.8.0         
[71] tools_4.4.0         ggsignif_0.6.4      cowplot_1.1.3       colorspace_2.1-1    cli_3.6.4          
[76] km.ci_0.5-6         textshaping_1.0.0   gtable_0.3.6        rstatix_0.7.2       sass_0.4.9         
[81] digest_0.6.37       BiocGenerics_0.50.0 rjson_0.2.21        farver_2.1.2        htmltools_0.5.8.1  
[86] lifecycle_1.0.4     GlobalOptions_0.1.2 gridtext_0.1.5      bit64_4.6.0-1       MASS_7.3-64        
LS0tCnRpdGxlOiAiUnVuIExHRyBhbmQgSEdHIHN1cnZpdmFsIGJ5IHNwbGljaW5nIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgc3BsaWNpbmcgYnVyZGVuIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQphdXRob3I6IFJ5YW4gQ29yYmV0dApkYXRlOiAyMDI0CnBhcmFtczoKICBwbG90X2NpOiBUUlVFCi0tLQoKKipQdXJwb3NlOioqIAoKUnVucyBzdXJ2aXZhbCBhbmFseXNpcyBtb2RlbHMgdXNpbmcgc3BsaWNpbmcgY2x1c3RlciBhc3NpZ25tZW50IGFuZCAxKSBzaW5nbGUgZXhvbiBzcGxpY2luZyBidXJkZW4gaW5kZXggKFNCSSkgb3IgMikgS0VHRyBTcGxpY2Vvc29tZSBHU1ZBIHNjb3JlcyBhcyBhIHByZWRpY3RvcgoKIyMgVXNhZ2UgCgpVc2VzIGEgd3JhcHBlciBmdW5jdGlvbiAoYHN1cnZpdmFsX2FuYWx5c2lzYCkgZnJvbSB1dGlscyBmb2xkZXIuIAoKIyMgU2V0dXAKCiMjIyMgUGFja2FnZXMgYW5kIGZ1bmN0aW9ucwoKTG9hZCBwYWNrYWdlcywgc2V0IGRpcmVjdG9yeSBwYXRocyBhbmQgY2FsbCBzZXR1cCBzY3JpcHQKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzdXJ2aXZhbCkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCgpyb290X2RpciA8LSBycHJvanJvb3Q6OmZpbmRfcm9vdChycHJvanJvb3Q6Omhhc19kaXIoIi5naXQiKSkKCmRhdGFfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwgImRhdGEiKQphbmFseXNpc19kaXIgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLCAiYW5hbHlzZXMiLCAic3Vydml2YWwiKQppbnB1dF9kaXIgPC0gZmlsZS5wYXRoKGFuYWx5c2lzX2RpciwgInJlc3VsdHMiKQpyZXN1bHRzX2RpciA8LSBmaWxlLnBhdGgoYW5hbHlzaXNfZGlyLCAicmVzdWx0cyIpCnBsb3RfZGlyIDwtIGZpbGUucGF0aChhbmFseXNpc19kaXIsICJwbG90cyIpCgojIElmIHRoZSBpbnB1dCBhbmQgcmVzdWx0cyBkaXJlY3RvcmllcyBkbyBub3QgZXhpc3QsIGNyZWF0ZSBpdAppZiAoIWRpci5leGlzdHMocmVzdWx0c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShyZXN1bHRzX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQoKc291cmNlKGZpbGUucGF0aChhbmFseXNpc19kaXIsICJ1dGlsIiwgInN1cnZpdmFsX21vZGVscy5SIikpCmBgYAoKU2V0IG1ldGFkYXRhIGFuZCBjbHVzdGVyIGFzc2lnbm1lbnQgZmlsZSBwYXRocwoKYGBge3Igc2V0IHBhdGhzfQptZXRhZGF0YV9maWxlIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsICJzcGxpY2luZ19pbmRpY2VzX3dpdGhfc3Vydml2YWwudHN2IikKCmNsdXN0ZXJfZmlsZSA8LSBmaWxlLnBhdGgocm9vdF9kaXIsICJhbmFseXNlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZS1wc2ktY2x1c3RlcmluZyIsICJyZXN1bHRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlLWNsdXN0ZXItbWV0YWRhdGEtdG9wLTUwMDAtZXZlbnRzLXN0cmFuZGVkLnRzdiIpCgprZWdnX3Njb3Jlc19zdHJhbmRlZF9maWxlIDwtIGZpbGUucGF0aChyb290X2RpciwgImFuYWx5c2VzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlLXBzaS1jbHVzdGVyaW5nIiwgInJlc3VsdHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJnc3ZhX291dHB1dF9zdHJhbmRlZC50c3YiKQpgYGAKCldyYW5nbGUgZGF0YSAKQWRkIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgc3BsaWNlb3NvbWUgZ3N2YSBzY29yZXMgdG8gYG1ldGFkYXRhYCBhbmQgZGVmaW5lIGNvbHVtbiBgbGdnX2dyb3VwYCAoTEdHIG9yIG5vbl9MR0cpCgpgYGB7cn0KbWV0YWRhdGEgPC0gcmVhZF90c3YobWV0YWRhdGFfZmlsZSkKCmNsdXN0ZXJzIDwtIHJlYWRfdHN2KGNsdXN0ZXJfZmlsZSkgJT4lCiAgZHBseXI6OnJlbmFtZShLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gc2FtcGxlX2lkKQoKZ3N2YV9zY29yZXMgPC0gcmVhZF90c3Yoa2VnZ19zY29yZXNfc3RyYW5kZWRfZmlsZSkgJT4lCiAgZHBseXI6OmZpbHRlcihnZW5lc2V0ID09ICJLRUdHX1NQTElDRU9TT01FIikgJT4lCiAgZHBseXI6OnJlbmFtZShzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlID0gc2NvcmUpCgojIGhvdyBtYW55IGNsdXN0ZXJzPwpuX2NsdXN0IDwtIGxlbmd0aCh1bmlxdWUoY2x1c3RlcnMkY2x1c3RlcikpCgptZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUKICByaWdodF9qb2luKGNsdXN0ZXJzICU+JSBkcGx5cjo6c2VsZWN0KEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXIpKSAlPiUKICBsZWZ0X2pvaW4oZ3N2YV9zY29yZXMgJT4lIGRwbHlyOjpzZWxlY3Qoc2FtcGxlX2lkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlKSwKICAgICAgICAgICAgYnkgPSBjKCJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIiA9ICJzYW1wbGVfaWQiKSkgJT4lIAogIGRwbHlyOjptdXRhdGUoY2x1c3RlciA9IGdsdWU6OmdsdWUoIkNsdXN0ZXIge2NsdXN0ZXJ9IikpICU+JQogIGRwbHlyOjptdXRhdGUoY2x1c3RlciA9IGZjdF9yZWxldmVsKGNsdXN0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJDbHVzdGVyICIsIDE6bl9jbHVzdCkpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGxnZ19ncm91cCA9IGNhc2Vfd2hlbigKICAgIEhpc3RvbG9neSA9PSAiTG93LWdyYWRlIGdsaW9tYSIgfiAiTEdHIiwKICAgIFRSVUUgfiAibm9uLUxHRyIKICApKSAlPiUKICBkcGx5cjo6bXV0YXRlKFNCSSA9IFNJX1RvdGFsICogMTApICU+JQogIGRwbHlyOjptdXRhdGUoYWdlX2F0X2RpYWdub3Npc195ZWFycyA9IGFnZV9hdF9kaWFnbm9zaXNfZGF5cy8zNjUuMjUpCmBgYAoKR2VuZXJhdGUgY294cGggbW9kZWxzIGluY2x1ZGluZyBleHRlbnQgb2YgdHVtb3IgcmVzZWN0aW9uLCBsZ2cgZ3JvdXAsIGFuZCBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIFNCSSBhcyBjb3ZhcmlhdGVzCgpgYGB7cn0KYWRkX21vZGVsX29zIDwtIGZpdF9zYXZlX21vZGVsKG1ldGFkYXRhWyFtZXRhZGF0YSRleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uICVpbiUgYygiTm90IFJlcG9ydGVkIiwgIlVuYXZhaWxhYmxlIiksXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVybXMgPSAiZXh0ZW50X29mX3R1bW9yX3Jlc2VjdGlvbitsZ2dfZ3JvdXArY2x1c3RlcithZ2VfYXRfZGlhZ25vc2lzX3llYXJzK1NCSSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb3hfT1NfYWRkaXRpdmVfdGVybXNfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyX1NCSS5SRFMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtdWx0aXZhcmlhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhcnNfY29sID0gIk9TX3llYXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXR1c19jb2wgPSAiT1Nfc3RhdHVzIikKCmZvcmVzdF9vcyA8LSBwbG90Rm9yZXN0KHJlYWRSRFMoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY294X09TX2FkZGl0aXZlX3Rlcm1zX3Jlc2VjdGlvbl9sZ2dfZ3JvdXBfY2x1c3Rlcl9TQkkuUkRTIikpKQoKZm9yZXN0X29zCgpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RfZGlyLCAiZm9yZXN0X2FkZF9PU19yZXNlY3Rpb25fbGdnX2dyb3VwX2NsdXN0ZXJfYXNzaWdubWVudF9TQkkucGRmIiksCiAgICAgICBmb3Jlc3Rfb3MsCiAgICAgICB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIsCiAgICAgICBkZXZpY2UgPSAicGRmIikKCmFkZF9tb2RlbF9lZnMgPC0gZml0X3NhdmVfbW9kZWwobWV0YWRhdGFbIW1ldGFkYXRhJGV4dGVudF9vZl90dW1vcl9yZXNlY3Rpb24gJWluJSBjKCJOb3QgUmVwb3J0ZWQiLCAiVW5hdmFpbGFibGUiKSxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXJtcyA9ICJleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uK2xnZ19ncm91cCtjbHVzdGVyK2FnZV9hdF9kaWFnbm9zaXNfeWVhcnMrU0JJIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9FRlNfYWRkaXRpdmVfdGVybXNfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyX1NCSS5SRFMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtdWx0aXZhcmlhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhcnNfY29sID0gIkVGU195ZWFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXNfY29sID0gIkVGU19zdGF0dXMiKQoKZm9yZXN0X2VmcyA8LSBwbG90Rm9yZXN0KHJlYWRSRFMoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY294X0VGU19hZGRpdGl2ZV90ZXJtc19yZXNlY3Rpb25fbGdnX2dyb3VwX2NsdXN0ZXJfU0JJLlJEUyIpKSkKCmZvcmVzdF9lZnMKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJmb3Jlc3RfYWRkX0VGU19yZXNlY3Rpb25fbGdnX2dyb3VwX2NsdXN0ZXJfYXNzaWdubWVudF9TQkkucGRmIiksCiAgICAgICBmb3Jlc3RfZWZzLAogICAgICAgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLAogICAgICAgZGV2aWNlID0gInBkZiIpCmBgYApyZXBlYXQgYW5hbHlzaXMsIHJlcGxhY2luZyBTQkkgd2l0aCBLRUdHIHNwbGljZW9zb21lIGdzdmEgc2NvcmUKCmBgYHtyfQphZGRfbW9kZWxfb3MgPC0gZml0X3NhdmVfbW9kZWwobWV0YWRhdGFbIW1ldGFkYXRhJGV4dGVudF9vZl90dW1vcl9yZXNlY3Rpb24gJWluJSBjKCJOb3QgUmVwb3J0ZWQiLCAiVW5hdmFpbGFibGUiKSxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXJtcyA9ICJleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uK2xnZ19ncm91cCtjbHVzdGVyK2FnZV9hdF9kaWFnbm9zaXNfeWVhcnMrc3BsaWNlb3NvbWVfZ3N2YV9zY29yZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb3hfT1NfYWRkaXRpdmVfdGVybXNfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyX3NwbGljZW9zb21lX3Njb3JlLlJEUyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm11bHRpdmFyaWF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyc19jb2wgPSAiT1NfeWVhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX2NvbCA9ICJPU19zdGF0dXMiKQoKZm9yZXN0X29zIDwtIHBsb3RGb3Jlc3QocmVhZFJEUyhmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb3hfT1NfYWRkaXRpdmVfdGVybXNfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyX3NwbGljZW9zb21lX3Njb3JlLlJEUyIpKSkKCmZvcmVzdF9vcwoKZ2dzYXZlKGZpbGUucGF0aChwbG90X2RpciwgImZvcmVzdF9hZGRfT1NfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyX2Fzc2lnbm1lbnRfc3BsaWNlb3NvbWVfc2NvcmUucGRmIiksCiAgICAgICBmb3Jlc3Rfb3MsCiAgICAgICB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIsCiAgICAgICBkZXZpY2UgPSAicGRmIikKYGBgCgoKYGBge3IgSW50ZXJhY3Rpb24gbW9kZWxzIHdpdGggR1NWQSBhbmQgU0JJfQptb2RlbHMgPC0gYygic3BsaWNlb3NvbWVfZ3N2YV9zY29yZSIsICJTQkkiKQoKZm9yIChlYWNoIGluIG1vZGVscykgewogIGludF9tb2RlbF9lZnMgPC0gZml0X3NhdmVfbW9kZWwobWV0YWRhdGFbIW1ldGFkYXRhJGV4dGVudF9vZl90dW1vcl9yZXNlY3Rpb24gJWluJSBjKCJOb3QgUmVwb3J0ZWQiLCAiVW5hdmFpbGFibGUiKSxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlcm1zID0gcGFzdGUwKCJleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uK2xnZ19ncm91cCtjbHVzdGVyKiIsIGVhY2gsICIrYWdlX2F0X2RpYWdub3Npc195ZWFycyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlLnBhdGgocmVzdWx0c19kaXIsIHBhc3RlMCgiY294X0VGU19pbnRlcmFjdGlvbl90ZXJtc19yZXNlY3Rpb25fbGdnX2dyb3VwX2NsdXN0ZXJfIiwgZWFjaCwgIi5SRFMiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtdWx0aXZhcmlhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyc19jb2wgPSAiRUZTX3llYXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX2NvbCA9ICJFRlNfc3RhdHVzIikKICAKICBpbnRfZm9yZXN0X2VmcyA8LSBwbG90Rm9yZXN0KHJlYWRSRFMoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCBwYXN0ZTAoImNveF9FRlNfaW50ZXJhY3Rpb25fdGVybXNfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyXyIsIGVhY2gsICIuUkRTIikpKSkKICAKICBpbnRfZm9yZXN0X2VmcwogIAogIGdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsIHBhc3RlMCgiZm9yZXN0X2ludF9FRlNfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyX2Fzc2lnbm1lbnRfIiwgZWFjaCwgIi5wZGYiKSksCiAgICAgICAgIGludF9mb3Jlc3RfZWZzLAogICAgICAgICB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIsCiAgICAgICAgIGRldmljZSA9ICJwZGYiKQoKICBpbnRfbW9kZWxfb3MgPC0gZml0X3NhdmVfbW9kZWwobWV0YWRhdGFbIW1ldGFkYXRhJGV4dGVudF9vZl90dW1vcl9yZXNlY3Rpb24gJWluJSBjKCJOb3QgUmVwb3J0ZWQiLCAiVW5hdmFpbGFibGUiKSxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlcm1zID0gcGFzdGUwKCJleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uK2xnZ19ncm91cCtjbHVzdGVyKiIsIGVhY2gsICIrYWdlX2F0X2RpYWdub3Npc195ZWFycyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlLnBhdGgocmVzdWx0c19kaXIsIHBhc3RlMCgiY294X09TX2ludGVyYWN0aW9uX3Rlcm1zX3Jlc2VjdGlvbl9sZ2dfZ3JvdXBfY2x1c3Rlcl8iLCBlYWNoLCAiLlJEUyIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm11bHRpdmFyaWF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXJzX2NvbCA9ICJPU195ZWFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXR1c19jb2wgPSAiT1Nfc3RhdHVzIikKICAKICBpbnRfZm9yZXN0X29zIDwtIHBsb3RGb3Jlc3QocmVhZFJEUyhmaWxlLnBhdGgocmVzdWx0c19kaXIsIHBhc3RlMCgiY294X09TX2ludGVyYWN0aW9uX3Rlcm1zX3Jlc2VjdGlvbl9sZ2dfZ3JvdXBfY2x1c3Rlcl8iLCBlYWNoLCAiLlJEUyIpKSkpCiAgCiAgaW50X2ZvcmVzdF9vcwogIAogIGdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsIHBhc3RlMCgiZm9yZXN0X2ludF9PU19yZXNlY3Rpb25fbGdnX2dyb3VwX2NsdXN0ZXJfYXNzaWdubWVudF8iLCBlYWNoLCAiLnBkZiIpKSwKICAgICAgICAgaW50X2ZvcmVzdF9vcywKICAgICAgICAgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLAogICAgICAgICBkZXZpY2UgPSAicGRmIikKICAKfQoKYWRkX21vZGVsX2VmcyA8LSBmaXRfc2F2ZV9tb2RlbChtZXRhZGF0YVshbWV0YWRhdGEkZXh0ZW50X29mX3R1bW9yX3Jlc2VjdGlvbiAlaW4lIGMoIk5vdCBSZXBvcnRlZCIsICJVbmF2YWlsYWJsZSIpLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlcm1zID0gImV4dGVudF9vZl90dW1vcl9yZXNlY3Rpb24rbGdnX2dyb3VwK2NsdXN0ZXIrYWdlX2F0X2RpYWdub3Npc195ZWFycytzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9FRlNfYWRkaXRpdmVfdGVybXNfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyX3NwbGljZW9zb21lX3Njb3JlLlJEUyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm11bHRpdmFyaWF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyc19jb2wgPSAiRUZTX3llYXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXR1c19jb2wgPSAiRUZTX3N0YXR1cyIpCgpmb3Jlc3RfZWZzIDwtIHBsb3RGb3Jlc3QocmVhZFJEUyhmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb3hfRUZTX2FkZGl0aXZlX3Rlcm1zX3Jlc2VjdGlvbl9sZ2dfZ3JvdXBfY2x1c3Rlcl9zcGxpY2Vvc29tZV9zY29yZS5SRFMiKSkpCgpmb3Jlc3RfZWZzCgpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RfZGlyLCAiZm9yZXN0X2FkZF9FRlNfcmVzZWN0aW9uX2xnZ19ncm91cF9jbHVzdGVyX2Fzc2lnbm1lbnRfc3BsaWNlb3NvbWVfc2NvcmUucGRmIiksCiAgICAgICBmb3Jlc3RfZWZzLAogICAgICAgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLAogICAgICAgZGV2aWNlID0gInBkZiIpCmBgYAoKU3Vic2V0IGBtZXRhZGF0YWAgZm9yIExHRywgYW5kIG9ubHkgaW5jbHVkZSBjbHVzdGVycyB3aXRoID49IDEwIHNhbXBsZXMKCmBgYHtyfQpsZ2cgPC0gbWV0YWRhdGEgJT4lCiAgZHBseXI6OmZpbHRlcihIaXN0b2xvZ3kgPT0gIkxvdy1ncmFkZSBnbGlvbWEiKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNsdXN0ZXIgPSBmYWN0b3IoY2x1c3RlcikpICU+JQogIGRwbHlyOjptdXRhdGUobW9sX3N1Yl9ncm91cCA9IGZjdF9yZWxldmVsKG1vbF9zdWJfZ3JvdXAsIGMoIldpbGR0eXBlIiwgIkJSQUYgVjYwMEUiLCAiQlJBRiBmdXNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk90aGVyIGFsdGVyYXRpb24iLCAiU0VHQSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKQoKcmV0YWluX2NsdXN0ZXJzX2xnZyA8LSBsZ2cgJT4lCiAgZHBseXI6OmNvdW50KGNsdXN0ZXIpICU+JQogIGZpbHRlcihuID49IDEwKSAlPiUKICBwdWxsKGNsdXN0ZXIpCgpsZ2cgPC0gbGdnICU+JQogIGZpbHRlcihjbHVzdGVyICVpbiUgcmV0YWluX2NsdXN0ZXJzX2xnZykgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNsdXN0ZXIgPSBmYWN0b3IoY2x1c3RlcikpCmBgYAoKR2VuZXJhdGUgY294cGggbW9kZWxzIGluY2x1ZGluZyBjb3ZhcmlhdGVzIGBleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uYCwgYG1vbF9zdWJfZ3JvdXBgLCBgY2x1c3RlcmAsIGFuZCBgU0JJYCBhbmQgcGxvdAoKYGBge3J9CiMgaWRlbnRpZnkgTEdHIGNsdXN0ZXJzCmxnZ19jbHVzdGVycyA8LSBtZXRhZGF0YSAlPiUKICBmaWx0ZXIobGdnX2dyb3VwID09ICJMR0ciKSAlPiUKICBtdXRhdGUoY2x1c3RlciA9IGFzLmludGVnZXIoZ3N1YigiY2x1c3RlciIsICIiLCBjbHVzdGVyKSkpICU+JQogIHB1bGwoY2x1c3RlcikgJT4lCiAgc29ydCgpICU+JQogIHVuaXF1ZSgpCgoKYWRkX21vZGVsX2xnZ19lZnMgPC0gZml0X3NhdmVfbW9kZWwobGdnWyFsZ2ckY2x1c3RlciAlaW4lIGxnZ19jbHVzdGVycyAmICFsZ2ckZXh0ZW50X29mX3R1bW9yX3Jlc2VjdGlvbiAlaW4lIGMoIk5vdCBSZXBvcnRlZCIsICJVbmF2YWlsYWJsZSIpLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlcm1zID0gImV4dGVudF9vZl90dW1vcl9yZXNlY3Rpb24rbW9sX3N1Yl9ncm91cCtjbHVzdGVyK2FnZV9hdF9kaWFnbm9zaXNfeWVhcnMrU0JJIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9sZ2dfRUZTX2FkZGl0aXZlX3Rlcm1zX3Jlc2VjdGlvbl9zdWJ0eXBlX2NsdXN0ZXJfU0JJLlJEUyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm11bHRpdmFyaWF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyc19jb2wgPSAiRUZTX3llYXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXR1c19jb2wgPSAiRUZTX3N0YXR1cyIpCgpmb3Jlc3RfbGdnX2VmcyA8LSBwbG90Rm9yZXN0KHJlYWRSRFMoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY294X2xnZ19FRlNfYWRkaXRpdmVfdGVybXNfcmVzZWN0aW9uX3N1YnR5cGVfY2x1c3Rlcl9TQkkuUkRTIikpKQoKZm9yZXN0X2xnZ19lZnMKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJmb3Jlc3RfYWRkX0VGU19MR0dfcmVzZWN0aW9uX3N1YnR5cGVfY2x1c3Rlcl9hc3NpZ25tZW50X1NCSS5wZGYiKSwKICAgICAgIGZvcmVzdF9sZ2dfZWZzLAogICAgICAgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLAogICAgICAgZGV2aWNlID0gInBkZiIpCmBgYAoKCnJlcGVhdCBhbmFseXNpcyByZXBsYWNpbmcgYFNCSWAgd2l0aCBgc3BsaWNlb3NvbWVfZ3N2YV9zY29yZWAKCmBgYHtyfQphZGRfbW9kZWxfbGdnX2VmcyA8LSBmaXRfc2F2ZV9tb2RlbChsZ2dbIWxnZyRjbHVzdGVyICVpbiUgbGdnX2NsdXN0ZXJzICYgIWxnZyRleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uICVpbiUgYygiTm90IFJlcG9ydGVkIiwgIlVuYXZhaWxhYmxlIiksXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVybXMgPSAiZXh0ZW50X29mX3R1bW9yX3Jlc2VjdGlvbittb2xfc3ViX2dyb3VwK2NsdXN0ZXIrYWdlX2F0X2RpYWdub3Npc195ZWFycytzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9sZ2dfRUZTX2FkZGl0aXZlX3Rlcm1zX3Jlc2VjdGlvbl9zdWJ0eXBlX2NsdXN0ZXJfc3BsaWNlb3NvbWVfc2NvcmUuUkRTIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibXVsdGl2YXJpYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXJzX2NvbCA9ICJFRlNfeWVhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX2NvbCA9ICJFRlNfc3RhdHVzIikKCmZvcmVzdF9sZ2dfZWZzIDwtIHBsb3RGb3Jlc3QocmVhZFJEUyhmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb3hfbGdnX0VGU19hZGRpdGl2ZV90ZXJtc19yZXNlY3Rpb25fc3VidHlwZV9jbHVzdGVyX3NwbGljZW9zb21lX3Njb3JlLlJEUyIpKSkKCmZvcmVzdF9sZ2dfZWZzCgpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RfZGlyLCAiZm9yZXN0X2FkZF9FRlNfTEdHX3Jlc2VjdGlvbl9zdWJ0eXBlX2NsdXN0ZXJfYXNzaWdubWVudF9zcGxpY2Vvc29tZV9zY29yZS5wZGYiKSwKICAgICAgIGZvcmVzdF9sZ2dfZWZzLAogICAgICAgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLAogICAgICAgZGV2aWNlID0gInBkZiIpCmBgYAoKU3Vic2V0IGBtZXRhZGF0YWAgZm9yIEhHRyBhbmQgcmV0YWluIGNsdXN0ZXIgd2l0aCBuID49IDEwCgpgYGB7cn0KaGdnIDwtIG1ldGFkYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIoSGlzdG9sb2d5ICVpbiUgYygiT3RoZXIgaGlnaC1ncmFkZSBnbGlvbWEiLCAiRElQRyBvciBETUciKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjbHVzdGVyID0gZmFjdG9yKGNsdXN0ZXIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKG1vbF9zdWJfZ3JvdXAgPSBmY3RfcmVsZXZlbChtb2xfc3ViX2dyb3VwLCBjKCJIR0csIEgzIHdpbGR0eXBlIiwgIkhHRywgSDMgd2lsZHR5cGUsIFRQNTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRNRywgSDMgSzI4IiwgIkRNRywgSDMgSzI4LCBUUDUzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJESEcsIEgzIEczNSIsICJESEcsIEgzIEczNSwgVFA1MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSEdHLCBJREgsIFRQNTMiLCAiSEdHLCBQWEEiLCAiSEdHLCBQWEEsIFRQNTMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJSEcsIEFMSy1hbHRlcmVkIiwgIklIRywgTlRSSy1hbHRlcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJSEcsIFJPUzEtYWx0ZXJlZCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKSAlPiUKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShPU19kYXlzKSB8ICFpcy5uYShFRlNfZGF5cykpCgpyZXRhaW5fY2x1c3RlcnNfaGdnIDwtIGhnZyAlPiUKICBkcGx5cjo6Y291bnQoY2x1c3RlcikgJT4lCiAgZmlsdGVyKG4gPj0gMTApICU+JQogIHB1bGwoY2x1c3RlcikKCmhnZyA8LSBoZ2cgJT4lCiAgZmlsdGVyKGNsdXN0ZXIgJWluJSByZXRhaW5fY2x1c3RlcnNfaGdnKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNsdXN0ZXIgPSBmYWN0b3IoY2x1c3RlcikpICU+JQogICAgZHBseXI6Om11dGF0ZShTSV9ncm91cCA9IGNhc2Vfd2hlbigKICAgICAgU0JJID4gc3VtbWFyeShTQkkpWyIzcmQgUXUuIl0gfiAiSGlnaCBTQkkiLAogICAgICBTQkkgPCBzdW1tYXJ5KFNCSSlbIjFzdCBRdS4iXSB+ICJMb3cgU0JJIiwKICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8KICAgICkpICU+JQogIGRwbHlyOjptdXRhdGUoc3BsaWNlb3NvbWVfZ3JvdXAgPSBjYXNlX3doZW4oCiAgICAgIHNwbGljZW9zb21lX2dzdmFfc2NvcmUgPiBzdW1tYXJ5KHNwbGljZW9zb21lX2dzdmFfc2NvcmUpWyIzcmQgUXUuIl0gfiAiU3BsaWNlIEdTVkEgNHRoIFEiLAogICAgICBzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlID4gc3VtbWFyeShzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlKVsiTWVkaWFuIl0gfiAiU3BsaWNlIEdTVkEgM3JkIFEiLAogICAgICBzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlID4gc3VtbWFyeShzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlKVsiMXN0IFF1LiJdIH4gIlNwbGljZSBHU1ZBIDJuZCBRIiwKICAgICAgVFJVRSB+ICJTcGxpY2UgR1NWQSAxc3QgUSIKICAgICkpICU+JQogIGRwbHlyOjptdXRhdGUoU0lfZ3JvdXAgPSBmY3RfcmVsZXZlbChTSV9ncm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkhpZ2ggU0JJIiwgIkxvdyBTQkkiKSkpICU+JQogIGRwbHlyOjptdXRhdGUoc3BsaWNlb3NvbWVfZ3JvdXAgPSBmY3RfcmVsZXZlbChzcGxpY2Vvc29tZV9ncm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIlNwbGljZSBHU1ZBIDFzdCBRIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTcGxpY2UgR1NWQSAybmQgUSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU3BsaWNlIEdTVkEgM3JkIFEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU3BsaWNlIEdTVkEgNHRoIFEiKSkpCmBgYAoKCkdlbmVyYXRlIEhHRyBLTSBtb2RlbHMgd2l0aCBgc3BsaWNlb3NvbWVfZ3JvdXBgIGFzIGNvdmFyaWF0ZQoKYGBge3J9CiMgR2VuZXJhdGUga2FwbGFuIG1laWVyIHN1cnZpdmFsIG1vZGVscyBmb3IgT1MgYW5kIEVGUywgYW5kIHNhdmUgb3V0cHV0cwpoZ2dfa2FwX29zIDwtIHN1cnZpdmFsX2FuYWx5c2lzKAogIG1ldGFkYXRhICA9IGhnZyAlPiUgZHBseXI6OmZpbHRlcighaXMubmEoc3BsaWNlb3NvbWVfZ3JvdXApKSwKICBpbmRfdmFyID0gInNwbGljZW9zb21lX2dyb3VwIiwKICB0ZXN0ID0gImthcC5tZWllciIsCiAgbWV0YWRhdGFfc2FtcGxlX2NvbCA9ICJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIiwKICBkYXlzX2NvbCA9ICJPU19kYXlzIiwKICBzdGF0dXNfY29sID0gIk9TX3N0YXR1cyIKKQoKcmVhZHI6OndyaXRlX3JkcyhoZ2dfa2FwX29zLAogICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImxvZ3JhbmtfaGdnX09TX3NwbGljZV9ncm91cC5SRFMiKSkKCmhnZ19rYXBfZWZzIDwtIHN1cnZpdmFsX2FuYWx5c2lzKAogIG1ldGFkYXRhICA9IGhnZyAlPiUgZHBseXI6OmZpbHRlcighaXMubmEoc3BsaWNlb3NvbWVfZ3JvdXApKSwKICBpbmRfdmFyID0gInNwbGljZW9zb21lX2dyb3VwIiwKICB0ZXN0ID0gImthcC5tZWllciIsCiAgbWV0YWRhdGFfc2FtcGxlX2NvbCA9ICJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIiwKICBkYXlzX2NvbCA9ICJFRlNfZGF5cyIsCiAgc3RhdHVzX2NvbCA9ICJFRlNfc3RhdHVzIgopCgpyZWFkcjo6d3JpdGVfcmRzKGhnZ19rYXBfZWZzLAogICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImxvZ3JhbmtfaGdnX0VGU19zcGxpY2VfZ3JvdXAuUkRTIikpCmBgYAoKR2VuZXJhdGUgS00gcGxvdHMKCmBgYHtyfQprbV9oZ2dfb3NfcGxvdCA8LSBwbG90S00obW9kZWwgPSBoZ2dfa2FwX29zLAogICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gInNwbGljZW9zb21lX2dyb3VwIiwKICAgICAgICAgICAgICAgICAgICBjb21iaW5lZCA9IEYsIAogICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkhHRywgb3ZlcmFsbCBzdXJ2aXZhbCIsCiAgICAgICAgICAgICAgICAgICAgcF9wb3MgPSAidG9wcmlnaHQiKQoKZ2dzYXZlKGZpbGUucGF0aChwbG90X2RpciwgImttX2hnZ19PU19zcGxpY2Vvc29tZV9zY29yZS5wZGYiKSwKICAgICAgIGttX2hnZ19vc19wbG90LAogICAgICAgd2lkdGggPSA5LCBoZWlnaHQgPSA1LCB1bml0cyA9ICJpbiIsCiAgICAgICBkZXZpY2UgPSAicGRmIikKCmttX2hnZ19lZnNfcGxvdCA8LSBwbG90S00obW9kZWwgPSBoZ2dfa2FwX2VmcywKICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJzcGxpY2Vvc29tZV9ncm91cCIsCiAgICAgICAgICAgICAgICAgICAgY29tYmluZWQgPSBGLCAKICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJIR0csIGV2ZW50LWZyZWUgc3Vydml2YWwiLAogICAgICAgICAgICAgICAgICAgIHBfcG9zID0gInRvcHJpZ2h0IikKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJrbV9oZ2dfRUZTX3NwbGljZW9zb21lX3Njb3JlLnBkZiIpLCAKICAgICAgIGttX2hnZ19lZnNfcGxvdCwKICAgICAgIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSwgdW5pdHMgPSAiaW4iLAogICAgICAgZGV2aWNlID0gInBkZiIpCmBgYAoKR2VuZXJhdGUgY294cGggbW9kZWxzIGZvciBIR0cgaW5jbHVkaW5nIGNvdmFyaWF0ZXMgYG1vbF9zdWJfZ3JvdXBgIGBjbHVzdGVyYCwgYW5kIGBTQklgLCBhbmQgcGxvdAoKYGBge3J9CmFkZF9tb2RlbF9oZ2dfb3MgPC0gZml0X3NhdmVfbW9kZWwoaGdnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXJtcyA9ICJtb2xfc3ViX2dyb3VwK2NsdXN0ZXIrYWdlX2F0X2RpYWdub3Npc195ZWFycytTQkkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY294X2hnZ19PU19hZGRpdGl2ZV90ZXJtc19zdWJ0eXBlX2NsdXN0ZXJfU0JJLlJEUyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm11bHRpdmFyaWF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyc19jb2wgPSAiT1NfeWVhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX2NvbCA9ICJPU19zdGF0dXMiKQoKZm9yZXN0X2hnZ19vcyA8LSBwbG90Rm9yZXN0KHJlYWRSRFMoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY294X2hnZ19PU19hZGRpdGl2ZV90ZXJtc19zdWJ0eXBlX2NsdXN0ZXJfU0JJLlJEUyIpKSkKCmZvcmVzdF9oZ2dfb3MKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJmb3Jlc3RfYWRkX09TX0hHR19zdWJ0eXBlX2NsdXN0ZXJfYXNzaWdubWVudF9TQkkucGRmIiksCiAgICAgICBmb3Jlc3RfaGdnX29zLAogICAgICAgd2lkdGggPSA5LCBoZWlnaHQgPSA1LCB1bml0cyA9ICJpbiIsCiAgICAgICBkZXZpY2UgPSAicGRmIikKCmFkZF9tb2RlbF9oZ2dfZWZzIDwtIGZpdF9zYXZlX21vZGVsKGhnZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVybXMgPSAibW9sX3N1Yl9ncm91cCtjbHVzdGVyK2FnZV9hdF9kaWFnbm9zaXNfeWVhcnMrU0JJIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9oZ2dfRUZTX2FkZGl0aXZlX3Rlcm1zX3N1YnR5cGVfY2x1c3Rlcl9TQkkuUkRTIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibXVsdGl2YXJpYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXJzX2NvbCA9ICJFRlNfeWVhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX2NvbCA9ICJFRlNfc3RhdHVzIikKCmZvcmVzdF9oZ2dfZWZzIDwtIHBsb3RGb3Jlc3QocmVhZFJEUyhmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb3hfaGdnX0VGU19hZGRpdGl2ZV90ZXJtc19zdWJ0eXBlX2NsdXN0ZXJfU0JJLlJEUyIpKSkKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJmb3Jlc3RfYWRkX0VGU19IR0dfc3VidHlwZV9jbHVzdGVyX2Fzc2lnbm1lbnRfU0JJLnBkZiIpLAogICAgICAgZm9yZXN0X2hnZ19lZnMsCiAgICAgICB3aWR0aCA9IDksIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwKICAgICAgIGRldmljZSA9ICJwZGYiKQpgYGAKClJlcGVhdCBhbmFseXNpcyByZXBsYWNpbmcgYFNCSWAgd2l0aCBgc3BsaWNlb3NvbWVfZ3N2YV9zY29yZWAKCmBgYHtyfQphZGRfbW9kZWxfaGdnX29zIDwtIGZpdF9zYXZlX21vZGVsKGhnZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVybXMgPSAibW9sX3N1Yl9ncm91cCtjbHVzdGVyK2FnZV9hdF9kaWFnbm9zaXNfeWVhcnMrc3BsaWNlb3NvbWVfZ3N2YV9zY29yZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb3hfaGdnX09TX2FkZGl0aXZlX3Rlcm1zX3N1YnR5cGVfY2x1c3Rlcl9zcGxpY2Vvc29tZV9zY29yZS5SRFMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtdWx0aXZhcmlhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhcnNfY29sID0gIk9TX3llYXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXR1c19jb2wgPSAiT1Nfc3RhdHVzIikKCmZvcmVzdF9oZ2dfb3MgPC0gcGxvdEZvcmVzdChyZWFkUkRTKGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9oZ2dfT1NfYWRkaXRpdmVfdGVybXNfc3VidHlwZV9jbHVzdGVyX3NwbGljZW9zb21lX3Njb3JlLlJEUyIpKSkKCmZvcmVzdF9oZ2dfb3MKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJmb3Jlc3RfYWRkX09TX0hHR19zdWJ0eXBlX2NsdXN0ZXJfYXNzaWdubWVudF9zcGxpY2Vvc29tZV9zY29yZS5wZGYiKSwKICAgICAgIGZvcmVzdF9oZ2dfb3MsCiAgICAgICB3aWR0aCA9IDksIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwKICAgICAgIGRldmljZSA9ICJwZGYiKQoKYWRkX21vZGVsX2hnZ19lZnMgPC0gZml0X3NhdmVfbW9kZWwoaGdnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXJtcyA9ICJtb2xfc3ViX2dyb3VwK2NsdXN0ZXIrYWdlX2F0X2RpYWdub3Npc195ZWFycytzcGxpY2Vvc29tZV9nc3ZhX3Njb3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9oZ2dfRUZTX2FkZGl0aXZlX3Rlcm1zX3N1YnR5cGVfY2x1c3Rlcl9zcGxpY2Vvc29tZV9zY29yZS5SRFMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtdWx0aXZhcmlhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhcnNfY29sID0gIkVGU195ZWFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXNfY29sID0gIkVGU19zdGF0dXMiKQoKZm9yZXN0X2hnZ19lZnMgPC0gcGxvdEZvcmVzdChyZWFkUkRTKGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9oZ2dfRUZTX2FkZGl0aXZlX3Rlcm1zX3N1YnR5cGVfY2x1c3Rlcl9zcGxpY2Vvc29tZV9zY29yZS5SRFMiKSkpCgpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RfZGlyLCAiZm9yZXN0X2FkZF9FRlNfSEdHX3N1YnR5cGVfY2x1c3Rlcl9hc3NpZ25tZW50X3NwbGljZW9zb21lX3Njb3JlLnBkZiIpLAogICAgICAgZm9yZXN0X2hnZ19lZnMsCiAgICAgICB3aWR0aCA9IDksIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwKICAgICAgIGRldmljZSA9ICJwZGYiKQpgYGAKRmlsdGVyIGZvciBjbHVzdGVyIDYKCmBgYHtyfQpjbHVzdGVyNl9kZiA8LSBtZXRhZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gIkNsdXN0ZXIgNiIsCiAgICAgICAgICAgICAgICAhaXMubmEoRUZTX2RheXMpKSAlPiUKICBkcGx5cjo6bXV0YXRlKFNJX2dyb3VwID0gY2FzZV93aGVuKAogICAgICBTQkkgPiBzdW1tYXJ5KFNCSSlbIjNyZCBRdS4iXSB+ICJIaWdoIFNCSSIsCiAgICAgIFNCSSA8IHN1bW1hcnkoU0JJKVsiMXN0IFF1LiJdIH4gIkxvdyBTQkkiLAogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXwogICAgKSkgJT4lCiAgZHBseXI6Om11dGF0ZShzcGxpY2Vvc29tZV9ncm91cCA9IGNhc2Vfd2hlbigKICAgICAgc3BsaWNlb3NvbWVfZ3N2YV9zY29yZSA+IHN1bW1hcnkoc3BsaWNlb3NvbWVfZ3N2YV9zY29yZSlbIjNyZCBRdS4iXSB+ICJTcGxpY2UgR1NWQSA0dGggUSIsCiAgICAgIHNwbGljZW9zb21lX2dzdmFfc2NvcmUgPiBzdW1tYXJ5KHNwbGljZW9zb21lX2dzdmFfc2NvcmUpWyJNZWRpYW4iXSB+ICJTcGxpY2UgR1NWQSAzcmQgUSIsCiAgICAgIHNwbGljZW9zb21lX2dzdmFfc2NvcmUgPiBzdW1tYXJ5KHNwbGljZW9zb21lX2dzdmFfc2NvcmUpWyIxc3QgUXUuIl0gfiAiU3BsaWNlIEdTVkEgMm5kIFEiLAogICAgICBUUlVFIH4gIlNwbGljZSBHU1ZBIDFzdCBRIgogICAgKSkgJT4lCiAgZHBseXI6Om11dGF0ZShTSV9ncm91cCA9IGZjdF9yZWxldmVsKFNJX2dyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiSGlnaCBTQkkiLCAiTG93IFNCSSIpKSkgJT4lCiAgZHBseXI6Om11dGF0ZShzcGxpY2Vvc29tZV9ncm91cCA9IGZjdF9yZWxldmVsKHNwbGljZW9zb21lX2dyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiU3BsaWNlIEdTVkEgMXN0IFEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNwbGljZSBHU1ZBIDJuZCBRIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTcGxpY2UgR1NWQSAzcmQgUSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTcGxpY2UgR1NWQSA0dGggUSIpKSkKYGBgCgpHZW5lcmF0ZSBLTSBtb2RlbHMgd2l0aCBgU0lfZ3JvdXBgIGFzIGNvdmFyaWF0ZQoKYGBge3J9CiMgR2VuZXJhdGUga2FwbGFuIG1laWVyIHN1cnZpdmFsIG1vZGVscyBmb3IgT1MgYW5kIEVGUywgYW5kIHNhdmUgb3V0cHV0cwpjNl9zaV9rYXBfb3MgPC0gc3Vydml2YWxfYW5hbHlzaXMoCiAgbWV0YWRhdGEgID0gY2x1c3RlcjZfZGYgJT4lIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKFNJX2dyb3VwKSksCiAgaW5kX3ZhciA9ICJTSV9ncm91cCIsCiAgdGVzdCA9ICJrYXAubWVpZXIiLAogIG1ldGFkYXRhX3NhbXBsZV9jb2wgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIsCiAgZGF5c19jb2wgPSAiT1NfZGF5cyIsCiAgc3RhdHVzX2NvbCA9ICJPU19zdGF0dXMiCikKCnJlYWRyOjp3cml0ZV9yZHMoYzZfc2lfa2FwX29zLAogICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImxvZ3JhbmtfY2x1c3RlcjZfT1NfU0JJLlJEUyIpKQoKYzZfc2lfa2FwX2VmcyA8LSBzdXJ2aXZhbF9hbmFseXNpcygKICBtZXRhZGF0YSAgPSBjbHVzdGVyNl9kZiAlPiUgZHBseXI6OmZpbHRlcighaXMubmEoU0lfZ3JvdXApKSwKICBpbmRfdmFyID0gIlNJX2dyb3VwIiwKICB0ZXN0ID0gImthcC5tZWllciIsCiAgbWV0YWRhdGFfc2FtcGxlX2NvbCA9ICJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIiwKICBkYXlzX2NvbCA9ICJFRlNfZGF5cyIsCiAgc3RhdHVzX2NvbCA9ICJFRlNfc3RhdHVzIgopCgpyZWFkcjo6d3JpdGVfcmRzKGM2X3NpX2thcF9lZnMsCiAgICAgICAgICAgICAgICAgZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAibG9ncmFua19jbHVzdGVyNl9FRlNfU0JJLlJEUyIpKQpgYGAKCkdlbmVyYXRlIENsdXN0ZXIgNiBLTSBTSV9ncm91cCBwbG90cwoKYGBge3J9CmttX2M2X3NpX29zX3Bsb3QgPC0gcGxvdEtNKG1vZGVsID0gYzZfc2lfa2FwX29zLAogICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gIlNJX2dyb3VwIiwKICAgICAgICAgICAgICAgICAgICBjb21iaW5lZCA9IEYsIAogICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkNsdXN0ZXIgNiwgb3ZlcmFsbCBzdXJ2aXZhbCIsCiAgICAgICAgICAgICAgICAgICAgcF9wb3MgPSAidG9wcmlnaHQiKQoKZ2dzYXZlKGZpbGUucGF0aChwbG90X2RpciwgImttX2NsdXN0ZXI2X09TX3NiaV9ncm91cC5wZGYiKSwKICAgICAgIGttX2M2X3NpX29zX3Bsb3QsCiAgICAgICB3aWR0aCA9IDgsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwKICAgICAgIGRldmljZSA9ICJwZGYiKQoKa21fYzZfc2lfZWZzX3Bsb3QgPC0gcGxvdEtNKG1vZGVsID0gYzZfc2lfa2FwX2VmcywKICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJTSV9ncm91cCIsCiAgICAgICAgICAgICAgICAgICAgY29tYmluZWQgPSBGLCAKICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJDbHVzdGVyIDYsIGV2ZW50LWZyZWUgc3Vydml2YWwiLAogICAgICAgICAgICAgICAgICAgIHBfcG9zID0gInRvcHJpZ2h0IikKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJrbV9jbHVzdGVyNl9FRlNfc2JpX2dyb3VwLnBkZiIpLCAKICAgICAgIGttX2M2X3NpX2Vmc19wbG90LAogICAgICAgd2lkdGggPSA4LCBoZWlnaHQgPSA1LCB1bml0cyA9ICJpbiIsCiAgICAgICBkZXZpY2UgPSAicGRmIikKYGBgCgoKR2VuZXJhdGUgS00gbW9kZWxzIHdpdGggYHNwbGljZW9zb21lX2dyb3VwYCBhcyBjb3ZhcmlhdGUKCmBgYHtyfQojIEdlbmVyYXRlIGthcGxhbiBtZWllciBzdXJ2aXZhbCBtb2RlbHMgZm9yIE9TIGFuZCBFRlMsIGFuZCBzYXZlIG91dHB1dHMKYzZfc3BsaWNlX2thcF9vcyA8LSBzdXJ2aXZhbF9hbmFseXNpcygKICBtZXRhZGF0YSAgPSBjbHVzdGVyNl9kZiAlPiUgCiAgICBkcGx5cjo6ZmlsdGVyKHNwbGljZW9zb21lX2dyb3VwICVpbiUgYygiU3BsaWNlIEdTVkEgNHRoIFEiLCAiU3BsaWNlIEdTVkEgMXN0IFEiKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKHNwbGljZW9zb21lX2dyb3VwID0gZmFjdG9yKHNwbGljZW9zb21lX2dyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlNwbGljZSBHU1ZBIDFzdCBRIiwgIlNwbGljZSBHU1ZBIDR0aCBRIikpKSwKICBpbmRfdmFyID0gInNwbGljZW9zb21lX2dyb3VwIiwKICB0ZXN0ID0gImthcC5tZWllciIsCiAgbWV0YWRhdGFfc2FtcGxlX2NvbCA9ICJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIiwKICBkYXlzX2NvbCA9ICJPU19kYXlzIiwKICBzdGF0dXNfY29sID0gIk9TX3N0YXR1cyIKKQoKcmVhZHI6OndyaXRlX3JkcyhjNl9zcGxpY2Vfa2FwX29zLAogICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImxvZ3JhbmtfY2x1c3RlcjZfT1Nfc3BsaWNlX2dyb3VwLlJEUyIpKQoKYzZfc3BsaWNlX2thcF9lZnMgPC0gc3Vydml2YWxfYW5hbHlzaXMoCiAgbWV0YWRhdGEgID0gY2x1c3RlcjZfZGYgJT4lIAogICAgZHBseXI6OmZpbHRlcihzcGxpY2Vvc29tZV9ncm91cCAlaW4lIGMoIlNwbGljZSBHU1ZBIDR0aCBRIiwgIlNwbGljZSBHU1ZBIDFzdCBRIikpICU+JQogICAgZHBseXI6Om11dGF0ZShzcGxpY2Vvc29tZV9ncm91cCA9IGZhY3RvcihzcGxpY2Vvc29tZV9ncm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJTcGxpY2UgR1NWQSAxc3QgUSIsICJTcGxpY2UgR1NWQSA0dGggUSIpKSksCiAgaW5kX3ZhciA9ICJzcGxpY2Vvc29tZV9ncm91cCIsCiAgdGVzdCA9ICJrYXAubWVpZXIiLAogIG1ldGFkYXRhX3NhbXBsZV9jb2wgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIsCiAgZGF5c19jb2wgPSAiRUZTX2RheXMiLAogIHN0YXR1c19jb2wgPSAiRUZTX3N0YXR1cyIKKQoKcmVhZHI6OndyaXRlX3JkcyhjNl9zcGxpY2Vfa2FwX2VmcywKICAgICAgICAgICAgICAgICBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJsb2dyYW5rX2NsdXN0ZXI2X0VGU19zcGxpY2VfZ3JvdXAuUkRTIikpCmBgYAoKR2VuZXJhdGUgQ2x1c3RlciA2IEtNIHNwbGljZW9zb21lX2dyb3VwIHBsb3RzCgpgYGB7cn0Ka21fYzZfc3BsaWNlX29zX3Bsb3QgPC0gcGxvdEtNKG1vZGVsID0gYzZfc3BsaWNlX2thcF9vcywKICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJzcGxpY2Vvc29tZV9ncm91cCIsCiAgICAgICAgICAgICAgICAgICAgY29tYmluZWQgPSBGLCAKICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJDbHVzdGVyIDYsIG92ZXJhbGwgc3Vydml2YWwiLAogICAgICAgICAgICAgICAgICAgIHBfcG9zID0gInRvcHJpZ2h0IikKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJrbV9jbHVzdGVyNl9PU19zcGxpY2VfZ3JvdXAucGRmIiksCiAgICAgICBrbV9jNl9zcGxpY2Vfb3NfcGxvdCwKICAgICAgIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSwgdW5pdHMgPSAiaW4iLAogICAgICAgZGV2aWNlID0gInBkZiIpCgprbV9jNl9zcGxpY2VfZWZzX3Bsb3QgPC0gcGxvdEtNKG1vZGVsID0gYzZfc3BsaWNlX2thcF9lZnMsCiAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSAic3BsaWNlb3NvbWVfZ3JvdXAiLAogICAgICAgICAgICAgICAgICAgIGNvbWJpbmVkID0gRiwgCiAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiQ2x1c3RlciA2LCBldmVudC1mcmVlIHN1cnZpdmFsIiwKICAgICAgICAgICAgICAgICAgICBwX3BvcyA9ICJ0b3ByaWdodCIpCgpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RfZGlyLCAia21fY2x1c3RlcjZfRUZTX3NwbGljZV9ncm91cC5wZGYiKSwgCiAgICAgICBrbV9jNl9zcGxpY2VfZWZzX3Bsb3QsCiAgICAgICB3aWR0aCA9IDksIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwKICAgICAgIGRldmljZSA9ICJwZGYiKQpgYGAKQXNzZXNzIEVGUyBhbmQgT1MgYnkgU0JJIG9yIHNwbGljZW9zb21lIEdTVkEgc2NvcmUgaW4gbXVsdGl2YXJpYXRlIG1vZGVscyBhbmQgZ2VuZXJhdGUgZm9yZXN0IHBsb3RzCgpgYGB7cn0KYWRkX21vZGVsX2M2X2VmcyA8LSBmaXRfc2F2ZV9tb2RlbChjbHVzdGVyNl9kZiAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uICE9ICJVbmF2YWlsYWJsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpY2Vvc29tZV9ncm91cCAlaW4lIGMoIlNwbGljZSBHU1ZBIDR0aCBRIiwgIlNwbGljZSBHU1ZBIDFzdCBRIikpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjptdXRhdGUoSGlzdG9sb2d5ID0gZmN0X3JlbGV2ZWwoSGlzdG9sb2d5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiT3RoZXIgaGlnaC1ncmFkZSBnbGlvbWEiLCAiQXR5cGljYWwgVGVyYXRvaWQgUmhhYmRvaWQgVHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRElQRyBvciBETUciLCAiRXBlbmR5bW9tYSIsICJNZXNlbmNoeW1hbCB0dW1vciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdGhlciBDTlMgZW1icnlvbmFsIHR1bW9yIiwgIkxvdy1ncmFkZSBnbGlvbWEiKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXJtcyA9ICJleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uK2FnZV9hdF9kaWFnbm9zaXNfeWVhcnMrSGlzdG9sb2d5K3NwbGljZW9zb21lX2dyb3VwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9oZ2dfRUZTX2FkZGl0aXZlX3Rlcm1zX3N1YnR5cGVfY2x1c3Rlcl9zcGxpY2Vvc29tZV9zY29yZS5SRFMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtdWx0aXZhcmlhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhcnNfY29sID0gIkVGU195ZWFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXNfY29sID0gIkVGU19zdGF0dXMiKQoKZm9yZXN0X2M2X3NwbGljZW9zb21lX2VmcyA8LSBwbG90Rm9yZXN0KHJlYWRSRFMoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY294X2hnZ19FRlNfYWRkaXRpdmVfdGVybXNfc3VidHlwZV9jbHVzdGVyX3NwbGljZW9zb21lX3Njb3JlLlJEUyIpKSkKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJmb3Jlc3RfYWRkX0VGU19jbHVzdGVyNl9oaXN0b2xvZ3lfcmVzZWN0aW9uX3NwbGljZW9zb21lX2dyb3VwLnBkZiIpLAogICAgICAgZm9yZXN0X2M2X3NwbGljZW9zb21lX2VmcywKICAgICAgIHdpZHRoID0gOSwgaGVpZ2h0ID0gNCwgdW5pdHMgPSAiaW4iLAogICAgICAgZGV2aWNlID0gInBkZiIpCgphZGRfbW9kZWxfYzZfb3MgPC0gZml0X3NhdmVfbW9kZWwoY2x1c3RlcjZfZGYgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKCFleHRlbnRfb2ZfdHVtb3JfcmVzZWN0aW9uICVpbiUgYygiTm90IFJlcG9ydGVkIiwgIlVuYXZhaWxhYmxlIikpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bXV0YXRlKEhpc3RvbG9neSA9IGZjdF9yZWxldmVsKEhpc3RvbG9neSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIk90aGVyIGhpZ2gtZ3JhZGUgZ2xpb21hIiwgIkF0eXBpY2FsIFRlcmF0b2lkIFJoYWJkb2lkIFR1bW9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRJUEcgb3IgRE1HIiwgIkVwZW5keW1vbWEiLCAiTWVzZW5jaHltYWwgdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT3RoZXIgQ05TIGVtYnJ5b25hbCB0dW1vciIsICJMb3ctZ3JhZGUgZ2xpb21hIikpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVybXMgPSAiZXh0ZW50X29mX3R1bW9yX3Jlc2VjdGlvbithZ2VfYXRfZGlhZ25vc2lzX3llYXJzK0hpc3RvbG9neStTQkkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY294X2hnZ19PU19hZGRpdGl2ZV90ZXJtc19zdWJ0eXBlX2NsdXN0ZXJfc2lfZ3JvdXAuUkRTIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibXVsdGl2YXJpYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXJzX2NvbCA9ICJPU195ZWFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXNfY29sID0gIk9TX3N0YXR1cyIpCgpmb3Jlc3RfYzZfc2lfb3MgPC0gcGxvdEZvcmVzdChyZWFkUkRTKGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNveF9oZ2dfT1NfYWRkaXRpdmVfdGVybXNfc3VidHlwZV9jbHVzdGVyX3NpX2dyb3VwLlJEUyIpKSkKCmdnc2F2ZShmaWxlLnBhdGgocGxvdF9kaXIsICJmb3Jlc3RfYWRkX09TX2NsdXN0ZXI2X2hpc3RvbG9neV9yZXNlY3Rpb25fc2kucGRmIiksCiAgICAgICBmb3Jlc3RfYzZfc2lfb3MsCiAgICAgICB3aWR0aCA9IDksIGhlaWdodCA9IDQsIHVuaXRzID0gImluIiwKICAgICAgIGRldmljZSA9ICJwZGYiKQpgYGAKCgpQcmludCBzZXNzaW9uIGluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYA==